#!/usr/bin/python
#***************************************************************************
# Copyright notice for this file:
#  Copyright (C) 2012 TheAxeMan
#  _______ _                               _    _
# |__   __| |     ___     /\          ___ | \  / |
#    | |  | |__  / _ \   /  \  __  _ / _ \|  \/  | __ _ _ __
#    | |  | '_ \|  __/  / /\ \ \ \/ |  __/| .  . |/ _` | '_ \
#    | |  | | | | (__/\/ ____ \ )  (| (__/| |\/| | (_| | | | |
#    |_|  |_| |_|\____/_/    \_._/\_.\____|_|  |_|\__,_|_| |_|
#
# Final Fantasy TAS scripts by TheAxeMan
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#***************************************************************************


# Note that this script needs some information from a Final Fantasy rom.
# See runPlannerLib for more details and how to specify the location.


import sys
if globals().has_key('init_modules'):
  for m in [x for x in sys.modules.keys() if x not in init_modules]:
    del(sys.modules[m])
else:
  init_modules = sys.modules.keys()


import sys
import time

from statusbar import StatusBar
from battlePlannerLib import BattleRandGenerator

from runPlannerLib import BattleHash
from runPlannerLib import BattleGroups
from runPlannerLib import BattleGroup
from runPlannerLib import BattleSetups



#this class contains regression information
class RunPlannerRegression:
  def __init__(self, bname_list, surprise_index, luck, num_alive=1, skips=None, start_seed=0, target_seed=-1, num_skips=1):
    self.recursionLevel = 0
    self.start_seed = start_seed
    self.target_seed = target_seed
    self.num_alive = num_alive
    self.skips = skips
    self.num_skips = num_skips
    #load up list of battles
    bg = BattleGroups()
    self.battleList = [bg.getBattleByName(name) for name in bname_list]

    #start up a rand generator
    self.rg = BattleRandGenerator()
    #load up surprise and run data
    self.bh = BattleHash()
    self.run_data = self.bh.getRunData(luck, self.num_alive)
    self.surprise_rolls = self.bh.createSurpriseData(surprise_index)

  #get large and small count for entry
  def getMixCounts(self, h):
    for num_large in range(3):
      for num_small in range(7):
        if num_large == 0 and num_small == 0: continue
        if h == self.bh.hashes[2][num_large][num_small]:
          return (num_large,num_small)


  #reduce options to improve performance
  #Build up new lists that only have certain results. Since waits
  #are sorted, add to list only if it beats others in some way.
  #entries in optimized list are (wait, cost, hash, preemptive flag)
  def reduceOptionsLocal(self, wait_list, cost_list, zero_wait_seed, b2):
    optimized_list = []
    for i in range(len(wait_list)):
      #print "optimizer is considering wait of",wait_list[i]
      self.rg.seed = zero_wait_seed
      self.rg.useUpRands(wait_list[i])
      #get battle hash
      h2 = b2.battleHashes[self.rg.seed]
      #get preemptive/surprise status
      self.rg.useUpRands(b2.seedsUsed[self.rg.seed])
      surprise_roll = self.surprise_rolls[self.rg.seed]
      if surprise_roll-b2.surpriseFactor >= 90:
        #print "preemptive!"
        preemptive2 = True
      elif surprise_roll-b2.surpriseFactor <= 10:
        #print "surprise! -",surprise_roll
        continue
      else:
        preemptive2 = False
      #now compare against entries in the optimized list
      #Figure out if there is an absolutely better entry already.
      #That is, preemptive flag must match and enemy layout must
      #be strictly better. This is more complicated for mix setup.
      #Because the wait_list is sorted, we know that anything that
      #has already been placed in the optimized this has a shorter
      #wait.
      optimal = True
      if b2.setup == BattleSetups.mix:
        (h_num_large, h_num_small) = self.getMixCounts(h2)
      for entry in optimized_list:
        #no comparison unless both preemptive or not preemptive
        if entry[3] != preemptive2: continue
        if b2.setup == BattleSetups.mix:
          (e_num_large, e_num_small) = self.getMixCounts(entry[2])
          if h_num_large >= e_num_large and h_num_small >= e_num_small:
            optimal = False
            break
        elif h2 >= entry[2]:
          optimal = False
          break
      #Ok, now we know if this wait value is optimal or not
      if optimal:
        entry = (wait_list[i], cost_list[i], h2, preemptive2)
        optimized_list.append(entry)

    #now we have the optimized list, so rebuild the wait and cost lists
    #print "optimization results:",optimized_list
    new_wait_list = []
    new_cost_list = []
    for entry in optimized_list:
      new_wait_list.append(entry[0])
      new_cost_list.append(entry[1])
    return (new_wait_list, new_cost_list)

  def createWaitAndCostLists(self, b):

    #get battle hash
    h = b.battleHashes[self.rg.seed]
    total_enemies = self.bh.totalEnemies[h]

    #four seeds for the battle determination
    self.rg.useUpRands(b.seedsUsed[self.rg.seed])

    #check surprise status
    surprise_roll = self.surprise_rolls[self.rg.seed]
    #if self.recursionLevel < 2:
    #  print "level",self.recursionLevel,"surprise roll is",surprise_roll,"and battle's factor is",b.surpriseFactor
    if surprise_roll-b.surpriseFactor >= 90:
      preemptive = True
    elif surprise_roll-b.surpriseFactor <= 10:
      #surprise! return an empty list to indicate a problem
      return (range(256), [600 for w in xrange(256)], False, 0)
    else:
      preemptive = False

    #one seed for the surprise roll
    #two seeds for the minimum delay
    self.rg.useUpRands(1+self.num_alive*2)

    #Now figure out possible number of seeds to wait before running.
    #Also initialize list of costs for each possible run seed.
    #Initialize cost result and "other cost" result as well.
    #Ensure that list is sorted!
    if preemptive:
      #any seed is good in a preemptive battle!
      #The generation method here produces a sorted list
      wait_list = range(256)
      #Here is the cost for a preemptive! 19 frames.
      cost_list = [x*2+19 + total_enemies for x in wait_list]
    else:
      if len(self.run_data[h]) == 0:
        return (total_enemies, [])
      wait_list = [(x-self.rg.seed+256)%256 for x in self.run_data[h]]
      wait_list.sort()
      cost_list = [x*2 + total_enemies for x in wait_list]

    if preemptive:
      #use up 34 for ordering, no roll for run
      self.rg.useUpRands(34)
    else:
      #use up 34 for ordering and one for chance to run
      self.rg.useUpRands(35)
    zero_wait_seed = self.rg.seed

    return (wait_list, cost_list, preemptive, zero_wait_seed)


  #run a regression. Returns cost in frames and a list of waits for each fight.
  #Cost is calculated as total number of enemies plus 29 frames for each
  #preemptive battle.
  def recursiveRun(self):
    if len(self.battleList) == 0: return (0, [])

    b = self.battleList[0]
    (wait_list, cost_list, preemptive, zero_wait_seed) = \
                    self.createWaitAndCostLists(b)

    #if this was the last battle, return result: shortest wait
    if len(self.battleList) == 1:
      best_i = 0
      #figure in wait for target seed
      #print "costs:",cost_list
      if self.target_seed != -1:
        #print total_enemies,"total enemies"
        #print "target seed calculation..."
        for i in range(len(wait_list)):
          self.rg.seed = zero_wait_seed
          self.rg.useUpRands(wait_list[i])
          target_wait = (self.target_seed - self.rg.seed + 256) % 256
          #wait_list[i] += target_wait
          cost_list[i] += 2 * target_wait
          if cost_list[i] < cost_list[best_i]: best_i = i
      #print "costs:",cost_list
      return (cost_list[best_i], [wait_list[best_i]*2])

    #there are more battles to go, so recurse!
    #pop off first battle to prepare.
    b = self.battleList.pop(0)

    #print "before optimization:",wait_list
    #reduce options to improve performance
    (wait_list, cost_list) = self.reduceOptionsLocal(wait_list, cost_list, zero_wait_seed, self.battleList[0])

    best_cost = -1
    best_list = []
    self.recursionLevel += 1
    for i in range(len(wait_list)):
      if self.recursionLevel < 4:
        print "level",self.recursionLevel,"- recursion",i+1,"of",len(wait_list)
      self.rg.seed = zero_wait_seed
      self.rg.useUpRands(wait_list[i])
      (recurse_cost, recurse_list) = self.recursiveRun()
      #if the recurse_list is empty it means there was a surprise or other problem
      if len(recurse_list) == 0: continue
      #no problem, so add up cost
      cost_list[i] += recurse_cost
      if best_cost == -1 or cost_list[i] < best_cost:
        best_cost = cost_list[i]
        best_list = recurse_list
        best_list.insert(0, wait_list[i]*2)
    #if self.recursionLevel == 1:
    #  print cost_list
    #  print best_cost

    self.recursionLevel -= 1
    self.battleList.insert(0, b)
    #Note that if all possibilities were dead ends, best_list will be []
    #to indicate the problem.
    return (best_cost, best_list)
  
  def runWithSkip(self):
    bestCost, bestWaitlist = self.loopingRun()
    bestSkip = tuple()
    if self.skips is None or self.num_skips < 1:
      return (bestCost, bestWaitlist, bestSkip)
    
    print "\nCost with no skips is",bestCost,"\n"
    
    originalBattleList = self.battleList
    for i in xrange(len(self.battleList)):
      if not self.skips[i]:
        continue
      self.battleList = originalBattleList[:i] + originalBattleList[i+1:]
      cost, waitlist = self.loopingRun()
      
      print "\nSkip battle",i+1,"for cost",cost,"\n"
      
      #With target seed the cost will include extra wait time.
      #If it is close then I have to think more.
      if cost < bestCost or bestCost is None:
        bestCost = cost
        bestWaitlist = waitlist
        bestSkip = (i,)
        
    if self.num_skips < 2:
      self.battleList = originalBattleList
      return (bestCost, bestWaitlist, bestSkip)
    
    print "\nCost with one or no skips is",bestCost,"\n"
    
    #Now try two skips
    for i in xrange(len(originalBattleList)):
      if not self.skips[i]:
        continue
      for j in xrange(i+1, len(originalBattleList)):
        if not self.skips[j]:
          continue
        ###Special hack for AtoV
        #if i < 14 and j < 14:
          #continue
        #End special hack
        self.battleList = originalBattleList[:i] + \
                          originalBattleList[i+1:j] + \
                          originalBattleList[j+1:]
        cost, waitlist = self.loopingRun()
      
        print "\nSkip battle",i+1,"and",j+1,"for cost",cost,"\n"
      
        #With target seed the cost will include extra wait time.
        #If it is close then I have to think more.
        if cost < bestCost or bestCost is None:
          bestCost = cost
          bestWaitlist = waitlist
          bestSkip = (i,j)
          
    if self.num_skips < 3:
      self.battleList = originalBattleList
      return (bestCost, bestWaitlist, bestSkip)
    
    #Now for three
    for i in xrange(len(originalBattleList)):
      if not self.skips[i]:
        continue
      for j in xrange(i+1, len(originalBattleList)):
        if not self.skips[j]:
          continue
        for k in xrange(j+1, len(originalBattleList)):
          if not self.skips[k]:
            continue
          self.battleList = originalBattleList[:i] + \
                            originalBattleList[i+1:j] + \
                            originalBattleList[j+1:k] + \
                            originalBattleList[k+1:]
          cost, waitlist = self.loopingRun()
      
          print "\nSkip battle %d, %d and %d for cost %d\n" % (i,j,k,cost)
      
          #With target seed the cost will include extra wait time.
          #If it is close then I have to think more.
          if cost < bestCost or bestCost is None:
            bestCost = cost
            bestWaitlist = waitlist
            bestSkip = (i,j,k)
          
    self.battleList = originalBattleList
    return (bestCost, bestWaitlist, bestSkip)

  def loopingRun(self):
    #Results structure has an entry for each possible seed
    #after one battle and before the next. Each entry has
    #a wait list and a cost. Uninitializied entry is ([],-1).
    #To start out, the cost for the start seed will be set
    #to 0.
    
    self.rg.seed = self.start_seed
    results = [ ([],-1) for x in range(256) ]
    results[self.rg.seed] = ([], 0)

    for b in range(len(self.battleList)):
      print "Analyzing battle",b+1,"of",len(self.battleList)
      #print "current results:",results
      current_battle = self.battleList[b]
      new_results = [ ([],-1) for x in range(256) ]
      for start_seed in range(256):
        #a negative cost value indicates a bad entry
        if results[start_seed][1] < 0: continue

        #get results for this start seed
        self.rg.seed = start_seed
        (wait_list, cost_list, preemptive, zero_wait_seed) = \
                    self.createWaitAndCostLists(current_battle)
        #if b == 0:
        #  print "wait_list:",wait_list
        #  print "cost_list:",cost_list

        #Look at next battle and reduce options.
        if b < len(self.battleList)-1:
          #print "before optimization:",wait_list
          next_battle = self.battleList[b+1]
          (wait_list, cost_list) = self.reduceOptionsLocal(wait_list, cost_list, zero_wait_seed, next_battle)

        #if b >= 0:
        #  print "after optimization..."
        #  print "wait_list:",wait_list
        #  print "cost_list:",cost_list
        #create the next wait list from a copy
        full_wait_list = results[start_seed][0][:]
        #append a placeholder
        full_wait_list.append(-1)
        previous_cost = results[start_seed][1]
        #Now go through results, storing any that are the
        #lowest cost for each ending seed.
        for i in range(len(wait_list)):
          #assemble the full wait list and cost
          full_wait_list[-1] = wait_list[i]
          full_cost = previous_cost + cost_list[i]
          #figure out the best cost so far
          end_seed = (zero_wait_seed + wait_list[i]) % 256
          best_cost = new_results[end_seed][1]
          #check results and update if appropriate
          if best_cost == -1 or full_cost < best_cost:
            new_results[end_seed] = (full_wait_list[:], full_cost)
      #update results
      results = new_results

    #Now we have a full result list. Figure in wait time to
    #target seed if applicable to calculate full cost.
    best_wait_list = []
    best_cost = -1
    for end_seed in range(256):
      wait_list = results[end_seed][0]
      if self.target_seed == -1:
        cost = results[end_seed][1]
      else:
        target_cost = 2 * ((self.target_seed - end_seed + 256) % 256)
        cost = results[end_seed][1] + target_cost
      if best_cost == -1 or cost < best_cost:
        best_wait_list = wait_list
        best_cost = cost

    return (best_cost, [x*2 for x in best_wait_list])
      

def main():
  num_alive = 1  #this is generally the case...
  target_seed = -1  #keep -1 unless trying to do something...
  skip_list = None
  num_skips = 1
  if 0:    #pirates to wizards
    num_alive = 2
    surprise_index = 1
    luck = 6
    group_list = "5Cb,12a,7b,7b,Ba,1b,4b,66a,10a".split(",")
    skip_list =  "1   1   1  1  1  0  0   0   0".split()
    skip_list = [bool(int(i)) for i in skip_list]
    num_skips = 3
    #skip_list = None
    #start_seed = 220  #Faster running to pirates
    #start_seed = 126  #Preemptive to pirates
    start_seed = 142  #Preemptive to pirates, no int
    #start_seed = 110  #Luck on Garland as in 2008
    #start_seed = 166  #Solo after pirates
    target_seed = 100 #For standard fight vs wizards
    #target_seed = 19 #Open with crit vs wizards
    #target_seed = 23 #For hand axe kill
    #target_seed = 170 #For solo
  if 0:    #wizards to astos
    surprise_index = 1
    luck = 7
    group_list = "66a,5b,5b,4b".split(",")
    group_list += "Ba,13a,Ca".split(",")  #out of marsh cave
    skip_list = "0 0 0 0 1 1 1".split()
    skip_list = [bool(int(s)) for s in skip_list]
    num_skips = 2
    #For hand axe run the last one might come after Astos
    #start_seed = 20 #use seed 183 on wizard
    start_seed = 120 #use seed 27 on wizard
    #start_seed = 114 #use seed 21 on wizard
    target_seed = 50
  if 0:    #astos to vampire
    surprise_index = 2
    luck = 7
    group_list = []
    start_seed = 186 # after Astos
    
    if 0:
      #start_seed = 35 #attack asp at 196, preemptive
      start_seed = 38 #attack asp at 199, no preemptive
      luck = 8
      group_list +=    "Ca 12a 5Ba 0a Da 1a 0a 5Db Ba 5Bb 5Ca 12a 3b Da".split(" ") #Kill asp
      #target_seed = 252
    else:
      group_list += "Da Ca 12a 5Ba 0a Da 1a 0a 5Db Ba 5Bb 5Ca 12a 3b Da".split(" ")
      #group_list += "Da Ca 12a 5Ba 0a Da 1a 0a 5Db Ba 5Bb 5Ca 12a Da".split(" ") #skip wolves fight by dwarf cave
      #group_list += "Da Ca 12a 5Ba 0a Da 1a 0a 5Db Ba 5Ca 12a 3b Da".split(" ")  #skip sahag fight
    skip_list = [1 for i in group_list]
    #skip_list = [0 for i in group_list]
    #skip_list[-1] = 1
    #get on ship to Melmond
    group_list += "19a 8b Db 11b 10b 10b 18a Fb Fb".split(" ")
    skip_list  += "1   1  0  0   0   0   0   0  0".split()
    #Move coctrice?
    #group_list += "19a 8b Db 11b 10b 10b 18a Fb Fb 16a".split(" ")
    #skip_list  += "1   1  0  0   0   0   0   0  0  0".split()
    #group_list += "19a Db 11b 10b 10b 18a Fb Fb".split(" ") #Skip last fight before Earth Cave
    #skip_list  += "1   0  0   0   0   0   0  0".split()
    skip_list = [bool(int(s)) for s in skip_list]
    num_skips = 3

    target_seed = 8  #Used in last run
    #target_seed = 186 #For 191 first hit
    #target_seed = 66  #For 71 first hit

  if 0:    #Vampire to Lich
    surprise_index = 2
    luck = 8
    group_list = "16a 16a 11b Fb 14b Db 15a Bb 13a 19a 13a Ab 13a 19a Ab".split(" ")
    skip_list =  "0   0   0   0  0   0  0   1  1   0   1   1  1   0   1".split()
    #Move coctrice fight?
    #group_list = "16a 11b Fb 14b Db 15a Bb 13a 19a 13a Ab 13a 19a Ab".split(" ")
    #skip_list =  "0   0   0  0   0  0   1  1   0   1   1  1   0   1".split()
    #power cycle, enter earth cave again
    group_list += "64a 13b 10b 14b 13b 13b 1Da 18a 18a 16a 16a 63a 1Da".split(" ")
    skip_list  += "0   0   0   0   0   0   0   0   0   0   0   0   0".split()
    skip_list = [bool(int(s)) for s in skip_list]
    #skip_list = None
    num_skips = 3

    start_seed = 133 #From  12,213 combo as in previous run
    #start_seed = 139 #From  12,219 combo for no preemptive
    #start_seed = 185 #From  71,  9 combo
    #start_seed = 50  #From 191,130 combo

    #target_seed = 136  #Used before, 66 damage hit start
    #target_seed = 152  #66 damage ICE2 start
    #target_seed = 169  #120 damage ICE2 start
    target_seed = 241  #156 damage ICE2 start

  if 1:    #Lich to Eye
    surprise_index = 2
    luck = 9
    group_list = "Ab Cb 5Ba 15b 1Ea 15b 60b 5Fa 5Fa 5Fb 2Bb 16b 2Bb 2Fa 18b 2Ca 2Ca 2Fa 2Bb 18b".split(" ")
    skip_list =  "0  0  1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   0   0".split()
    #Skip fourth from above
    #group_list = "Ab Cb 5Ba 1Ea 15b 60b 5Fa 5Fa 5Fb 2Bb 16b 2Bb 2Fa 18b 2Ca 2Ca 2Fa 2Bb 18b".split(" ")
    #skip_list =  "0  0  1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   0   0".split()
    #Move fight after Eye
    #group_list = "Ab Cb 5Ba 15b 1Ea 15b 60b 5Fa 5Fa 5Fb 2Bb 16b 2Bb 2Fa 18b 2Ca 2Ca 2Fa 2Bb".split(" ")
    #skip_list =  "0  0  1   1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   0".split()
    skip_list = [bool(int(s)) for s in skip_list]
    num_skips = 3
    start_seed = 187
    target_seed = 8  #Two hits with luck
    #target_seed = 72  #One hit with flame sword
  if 0:    #Eye to Kraken
    surprise_index = 2
    luck = 10
    #Full list
    group_list = "2Ca 2Fa 6Ca 2Bb Eb 5Fa 20a 60b 5Fa 5Cb 37b 70a 47a 5Ab 5Ab 5Aa 48a 72a 42b 61b 72a 5Aa 42b 43b 42b 61b 42b".split(" ")
    skip_list =  "0   0   0   0   1  1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0".split()
    #Skip a fight on river
    #group_list = "2Ca 2Fa 6Ca 2Bb Eb 20a 60b 5Fa 5Cb 37b 70a 47a 5Ab 5Ab 5Aa 48a 72a 42b 61b 72a 5Aa 42b 43b 42b 61b 42b".split(" ")
    #skip_list =  "0   0   0   0   1  1   1   1   1   1   1   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0".split()
    #Move fight to after Eye
    #group_list = "2Ca 2Bb 2Fa 6Ca 2Bb Eb 5Fa 20a 60b 5Fa 5Cb 37b 70a 47a 5Ab 5Ab 5Aa 48a 72a 42b 61b 72a 5Aa 42b 43b 42b 61b 42b".split(" ")
    #skip_list =  "0   0   0   0   0   1  1   1   1   1   1   1   1   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0".split()
    skip_list = [bool(int(s)) for s in skip_list]
    num_skips = 3
    start_seed = 17  #Luck on Eye
    #start_seed = 211  #One hit on Eye
    target_seed = 92
  if 0:    #Kraken to Blue Dragon
    surprise_index = 3
    luck = 12
    group_list = "62a 20b 3Fa 3Fa 4Fa 4Fa 3Fa 4Ab 4Ab 4Fa 23b 4Fa 65a".split(" ")
    skip_list =  "1   1   0   0   0   0   0   0   0   0   0   0   1".split()
    #these two could be changed
    group_list += "41a 71b".split(" ")
    #group_list += "19b 71b".split(" ")
    skip_list += "1   1".split()

    group_list += "2Db 1Eb 1Eb 3Da 2Db 71b 4Ca 68a 4Ca 3Eb".split(" ")
    skip_list  += "1   1   1   1   1   1   0   0   0   0".split()
    skip_list = [bool(int(s)) for s in skip_list]
    num_skips = 3

    start_seed = 61 #Best after Kraken
    target_seed = 211 #same Blue D fight as before
  if 0:    #Blue Dragon to Tiamat
    surprise_index = 3
    luck = 11
    group_list = "52a 52a 40a 24b 41b 35b 52b 41b 50a".split(" ")

    start_seed = 72
    target_seed = 1 #Bane kills at 2
  if 0:    #Tiamat to Kary
    surprise_index = 3
    luck = 11
    group_list = "3Db 17b 1Fa 24a 26a".split(" ")
    skip_list =  "1   0   0   0   0".split()
    skip_list = [bool(int(s)) for s in skip_list]
    num_skips = 1

    start_seed = 222  #s2 on Tiamat
    target_seed = 242 #the bane sword's sure kill
  if 0:    #TOFR Zombie D to Lich
    surprise_index = 3
    luck = 13
    group_list = "30b 3Bb 3Fb 40b 40b".split(" ")
    #start_seed = 111 #if using seed 2 on zombie D
    start_seed = 205 #if using seed 96 on zombie D
    target_seed = 242 #the bane sword's sure kill
  if 0:    #TOFR water
    surprise_index = 3
    luck = 14
    group_list = "44a 45b 45b".split(" ")
    start_seed = 207 #from bane sword sure kill + levelup!
    target_seed = 242 #the bane sword's sure kill
    
  regression = RunPlannerRegression(group_list, surprise_index, luck, num_alive, skip_list, start_seed, target_seed, num_skips)

  #(total_cost, wait_list) = regression.loopingRun()
  (total_cost, wait_list, skips) = regression.runWithSkip()

  report_file = open("run_results.txt", "w")
  #report_file = sys.stdout
  print >> report_file, """
input information:
surprise_index""",surprise_index,"""
luck""",luck,"""
groups""",group_list,"""
start_seed""",start_seed,"""
target_seed""",target_seed,"""
"""


  if len(wait_list) == 0:
    print >> report_file, "no solution found!"
  else:
    print >> report_file, "total cost was",total_cost,"and wait frames for each battle are",wait_list
    print >> report_file, "wait sum is",sum(wait_list)

  enemyTotal = 0
  preemptiveTotal = 0
  
  #now simulate and report results
  bh = BattleHash()
  party_rolls = bh.createSurpriseData(surprise_index)
  if len(skips) == 1:
    regression.battleList = regression.battleList[:skips[0]] + regression.battleList[skips[0]+1:]
    print >> report_file, "skipped battle",skips[0]+1
  elif len(skips) == 2:
    regression.battleList = regression.battleList[:skips[0]] + \
                            regression.battleList[skips[0]+1:skips[1]] + \
                            regression.battleList[skips[1]+1:]
    print >> report_file, "skipped battles",skips[0]+1,"and",skips[1]+1
  elif len(skips) == 3:
    regression.battleList = regression.battleList[:skips[0]] + \
                            regression.battleList[skips[0]+1:skips[1]] + \
                            regression.battleList[skips[1]+1:skips[2]] + \
                            regression.battleList[skips[2]+1:]
    print >> report_file, "skipped battles %d, %d and %d" % tuple([i+1 for i in skips])
    
  rd = bh.getRunData(luck)
  rg = BattleRandGenerator()
  rg.seed = start_seed
  for i in range(len(wait_list)):
    print >> report_file
    print >> report_file, "battle",i+1
    b = regression.battleList[i]
    #get battle hash
    h = b.battleHashes[rg.seed]
    total_enemies = bh.totalEnemies[h]
    print >> report_file, "met",total_enemies,"enemy" if total_enemies == 1 else "enemies"
    enemyTotal += total_enemies
    #four seeds for the battle determination
    rg.useUpRands(b.seedsUsed[rg.seed])
    #check surprise status
    surprise_roll = party_rolls[rg.seed]
    if surprise_roll-b.surpriseFactor >= 90:
      preemptive = True
      print >> report_file, "preemptive!"
      preemptiveTotal += 1
    else:
      preemptive = False
    if surprise_roll-b.surpriseFactor <= 10:
      print >> report_file, "surprise!"
      sys.exit(0)
    #one seed for the surprise roll
    rg.useUpRands(1)
    #two seeds per each living char for the minimum delay
    rg.useUpRands(2*num_alive)
    #now figure out run delay
    delay = wait_list[i]/2
    if wait_list[i] % 2 != 0: print >> report_file, "error: odd wait frames!"
    print >> report_file, "needed to wait",delay*2,"frames"
    #use up the delay seeds
    rg.useUpRands(delay)
    if preemptive:
      #use up 34 for ordering, no roll for run
      rg.useUpRands(34)
    else:
      #use up 34 for ordering and one for chance to run
      rg.useUpRands(35)
    print >> report_file, "rand seed is",rg.seed
  
  print >> report_file, "\n%d total enemies and %d preemptives" % (enemyTotal, preemptiveTotal)

if __name__ == "__main__":
  main()
